/**
 * PerlaController - part of PerlaGis Architecture
 *
 * Copyright (C) 2010 Ricardo Gonzalez (gomnaz@gmail.com)
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see http://www.gnu.org/licenses/
 */
package org.perla.managers;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.perla.entities.Attitude;
import org.perla.entities.Bibi;
import org.perla.entities.Crackmeter;
import org.perla.entities.Sensori;
import org.perla.entities.Temperature;
import org.perla.exceptions.WrongSensorTypeException;
import org.sql.entities.Sql;

/**
 * Classe statica che gestisce i valori dei sensori.
 * Implementa metodi interrogano il database su alcuni o tutti i valori di un
 * sensore.
 *
 * @author Ricardo Gonzalez
 * @author Eva Gjeci
 */
public class Value {

    /**
     * Restituisce la lista dei valori del sensore passato come parametro.
     * Il numero dei valori è limitato dal parametro <code>limit</code>
     * 
     * @param s il sensore
     * @param limit il numero massimo di valori
     * @param em l'entity manager
     * @return la lista dei valori
     */
    private static List<Object> get(Sensori s, int limit, EntityManager em) {
        Query q;
        if (limit == 0) {
            q = em.createQuery(initializeSQL(s).toString());
        } else {
            q = em.createQuery(initializeSQL(s).toString()).setMaxResults(limit);
        }
        List<Object> l = executeQuery(q);
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore passato come parametro.
     * 
     * @param s il sensore
     * @param limit il numero massimo di valori
     * @param em l'entity manager
     * @return la lista dei valori
     */
    private static List<Object> getAll(Sensori s, EntityManager em) {
        return get(s, 0, em);
    }

    /**
     * Restituisce la lista di tutti i valori appartenenti al sensore pasato 
     * come parametro e registrati tra le due date indicate.
     *
     * @param s il sensore
     * @param from data più vecchia
     * @param to data più recente
     * @param em l'entity manager
     * @return la lista dei valori
     */
    private static List<Object> get(Sensori s, long from, long to, EntityManager em) {
        Query q;
        q = em.createQuery(initializeSQL(s).addWhere("t." + s.getTiposensore().getNome() + "PK.timestamp", "" + from + "", ">=").addWhere("t." + s.getTiposensore().getNome() + "PK.timestamp", "" + to + "", "<=").toString());
        List<Object> l = executeQuery(q);
        return l;
    }

    /**
     * Restituisce l'ultimo valore registrato dal sensore passato come parametro.
     *
     * @param s il sensore
     * @param em l'entity manager
     * @return l'ultimo valore
     */
    private static Object getLast(Sensori s, EntityManager em) {
        List<Object> l = get(s, 1, em);
        if (l.isEmpty()) {
            return null;
        } else {
            return get(s, 1, em).get(0);
        }
    }

    /**
     * Inizializza la stringa SQL per richiedere i valori del sensore passato
     * come parametro.
     *
     * @param s il sensore
     * @return la stringa SQL
     */
    private static Sql initializeSQL(Sensori s) {
        String t = s.getTiposensore().getNome();
        t = t.substring(0, 1).toUpperCase() + t.substring(1).toLowerCase();
        /*
         * Correzione sul tipo dei sensori Bibi, che è diverso dall'Id dei sensori
         */
        if (t.toLowerCase().equals("geophone")) {
            t = "Bibi";
        }

        Sql sql = new Sql("t", t + " t").addWhere("t." + t.toLowerCase() + "PK.idNodo", s.getSensoriPK().getIdNodo()).addWhere("t." + t.toLowerCase() + "PK.idSensore", s.getSensoriPK().getIdSensore()).setOrder("t." + t.toLowerCase() + "PK.timestamp", true);

        return sql;
    }

    /**
     * Restituisce la lista dei valori che rispondono all'oggetto
     * <code>Query</code> passato come parametro.
     *
     * @param q l'oggetto query da eseguire
     * @return la lista dei valori
     */
    private static List<Object> executeQuery(Query q) {
        List<Object> l = q.getResultList();
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Attitude passato
     * come parametro.
     *
     * @param s il sensore
     * @param limit il numero massimo di valori
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Attitude
     */
    public static List<Attitude> getAttitudes(Sensori s, int limit, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Attitude");
        ArrayList<Attitude> l = new ArrayList<Attitude>();
        for (Object o : get(s, limit, em)) {
            l.add((Attitude) o);
        }
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Bibi passato come
     * parametro.
     *
     * @param s il sensore
     * @param limit il numero massimo di valori
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Bibi
     */
    public static List<Bibi> getBibis(Sensori s, int limit, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Geophone");
        ArrayList<Bibi> l = new ArrayList<Bibi>();
        for (Object o : get(s, limit, em)) {
            l.add((Bibi) o);
        }
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Crackmeter passato
     * come parametro.
     *
     * @param s il sensore
     * @param limit il numero massimo di valori
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore AttiCrackmetertude
     */
    public static List<Crackmeter> getCrackmeters(Sensori s, int limit, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Crackmeter");
        ArrayList<Crackmeter> l = new ArrayList<Crackmeter>();
        for (Object o : get(s, limit, em)) {
            l.add((Crackmeter) o);
        }
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Temperature passato
     * come parametro.
     *
     * @param s il sensore
     * @param limit il numero massimo di valori
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Temperature
     */
    public static List<Temperature> getTemperatures(Sensori s, int limit, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Temperature");
        ArrayList<Temperature> l = new ArrayList<Temperature>();
        for (Object o : get(s, limit, em)) {
            l.add((Temperature) o);
        }
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Attitude registrati
     * nell'intervallo indicato.
     *
     * @param s il sensore
     * @param from inizio intervallo
     * @param to fine intervallo
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Attitude
     */
    public static List<Attitude> getAttitudes(Sensori s, long from, long to, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Attitude");
        ArrayList<Attitude> l = new ArrayList<Attitude>();
        for (Object o : get(s, from, to, em)) {
            l.add((Attitude) o);
        }
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Bibi registrati
     * nell'intervallo indicato.
     *
     * @param s il sensore
     * @param from inizio intervallo
     * @param to fine intervallo
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Bibi
     */
    public static List<Bibi> getBibis(Sensori s, long from, long to, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Geophone");
        ArrayList<Bibi> l = new ArrayList<Bibi>();
        for (Object o : get(s, from, to, em)) {
            l.add((Bibi) o);
        }
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Crackmeter registrati
     * nell'intervallo indicato.
     *
     * @param s il sensore
     * @param from inizio intervallo
     * @param to fine intervallo
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore AttiCrackmetertude
     */
    public static List<Crackmeter> getCrackmeters(Sensori s, long from, long to, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Crackmeter");
        ArrayList<Crackmeter> l = new ArrayList<Crackmeter>();
        for (Object o : get(s, from, to, em)) {
            l.add((Crackmeter) o);
        }
        return l;
    }

    /**
     * Restituisce la lista di tutti i valori del sensore Temperature registrati
     * nell'intervallo indicato.
     *
     * @param s il sensore
     * @param from inizio intervallo
     * @param to fine intervallo
     * @param em l'entity manager
     * @return la lista dei valori
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Temperature
     */
    public static List<Temperature> getTemperatures(Sensori s, long from, long to, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Temperature");
        ArrayList<Temperature> l = new ArrayList<Temperature>();
        for (Object o : get(s, from, to, em)) {
            l.add((Temperature) o);
        }
        return l;
    }

    /**
     * Restituisce l'ultimo valore registrato dal sensore Attitude passato come
     * parametro.
     *
     * @param s il sensore
     * @param em l'entity manager
     * @return l'ultimo valore Attitude
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Attitude
     */
    public static Attitude getLastAttitude(Sensori s, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Attitude");
        Object o = getLast(s, em);
        if (o == null) {
            o = new Attitude();
        }
        Attitude a = (Attitude) o;
        return a;
    }

    /**
     * Restituisce l'ultimo valore registrato dal sensore Bibi passato come
     * parametro.
     *
     * @param s il sensore
     * @param em l'entity manager
     * @return l'ultimo valore Bibi
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Bibi
     */
    public static Bibi getLastBibi(Sensori s, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Geophone");
        Object o = getLast(s, em);
        if (o == null) {
            o = new Bibi();
        }
        Bibi a = (Bibi) o;
        return a;
    }

    /**
     * Restituisce l'ultimo valore registrato dal sensore Crackmeter passato
     * come parametro.
     *
     * @param s il sensore
     * @param em l'entity manager
     * @return l'ultimo valore Crackmeter
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Crackmeter
     */
    public static Crackmeter getLastCrackmeter(Sensori s, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Crackmeter");
        Object o = getLast(s, em);
        if (o == null) {
            o = new Crackmeter();
        }
        Crackmeter a = (Crackmeter) o;
        return a;
    }

    /**
     * Restituisce l'ultimo valore registrato dal sensore Temperature passato
     * come parametro.
     * 
     * @param s il sensore
     * @param em l'entity manager
     * @return l'ultimo valore Temperature
     * @throws WrongSensorTypeException se il sensore passato come parametro non è un sensore Temperature
     */
    public static Temperature getLastTemperature(Sensori s, EntityManager em) throws WrongSensorTypeException {
        Sensor.hasCorrectType(s, "Temperature");
        Object o = getLast(s, em);
        Temperature a;
        if (o == null) {
            o = new Temperature();
            a = (Temperature) o;
            a.setValue(new Double(0));
        } else {
            a = (Temperature) o;
        }
        return a;
    }
}
